﻿using OperationPlugins.Compositions;
using System;
using System.Collections.Concurrent;
using System.ComponentModel.Composition;
using System.Diagnostics;

namespace OperationPlugins
{
    public static class OperationContextManager
    {
        private static IOperationContextFactory _overrideFactory;
        private static IOperationContextFactory _defaultFactory;

        /// <summary>
        /// Gets the IOperationContextFactory.
        /// </summary>
        /// <returns>IOperationContextFactory</returns>
        public static IOperationContextFactory GetOperationContextFactory()
        {
            return _overrideFactory ?? _defaultFactory;
        }

        /// <summary>
        /// Sets the IOperationContextFactory to be used.
        /// </summary>
        /// <param name="contextFactory">The IOperationContextFactory to be used.</param>
        public static void SetOperationContextFactory(IOperationContextFactory contextFactory)
        {
            if (contextFactory == null)
            {
                throw new ArgumentNullException("contextFactory");
            }

            _overrideFactory = contextFactory;
        }

        /// <summary>
        /// Tells the framework that it should use the default IOperationContextFactory implementation.
        /// </summary>
        public static void UseDefaultOperationContextFactory()
        {
            _overrideFactory = null;
        }

        public static OperationContext GetOperationContext(OperationId operation, BranchId branch, IOperationContextFactory contextFactory)
        {
            if (operation == null)
            {
                throw new ArgumentNullException("operation");
            }

            if (branch == null)
            {
                throw new ArgumentNullException("branch");
            }

            if (contextFactory == null)
            {
                throw new ArgumentNullException("contextFactory");
            }

            string key = string.Format("{0}|{1}", operation.GetHashCode(), branch.GetHashCode());

            OperationContext context;

            if (ContextStorage.Get<ConcurrentDictionary<string, OperationContext>>().TryGetValue(key, out context) == false)
            {
                context = contextFactory.CreateOperationContext(operation, branch);

                if (ContextStorage.Get<ConcurrentDictionary<string, OperationContext>>().TryAdd(key, context) == false)
                {
                    Debugger.Break();
                }
            }

            return context;
        }

        /// <summary>
        /// Initialize. 
        /// </summary>
        [Export(typeof(IInitialize))]
        private sealed class Initializer : IInitialize
        {
            void IInitialize.Initialize(InitializationContext context)
            {
                _defaultFactory = context.CompositionContainer.GetPlugin<IOperationContextFactory>();
            }
        }

        /// <summary>
        /// The default operation context factory. 
        /// </summary>
        [Export(typeof(IOperationContextFactory))]
        [DefaultPriority]
        private sealed class DefaultOperationContextFactory : IOperationContextFactory
        {
            OperationContext IOperationContextFactory.CreateOperationContext(OperationId operation, BranchId branch)
            {
                if (operation == null)
                {
                    throw new ArgumentNullException("operation");
                }

                if (branch == null)
                {
                    throw new ArgumentNullException("branch");
                }

                return new OperationContext { Operation = operation, Branch = branch };
            }
        }
    }
}
